#include "connection.h"
#include "connectionclosedexception.h"
#include "client_parser.h"
#include "protocol.h"
#include <iostream>
#include <string>
#include <stdexcept>
#include <cstdlib>
#include <memory>
using namespace std;

int main(int argc, char* argv[]) {
	if (argc != 3) {
		cerr << "Usage: myclient host-name port-number" << endl;
		exit(1);
	}
	int port = -1;
	try {
		port = stoi(argv[2]);
	} catch (exception& e) {
		cerr << "Wrong port number. " << e.what() << endl;
		exit(1);
	}
	shared_ptr<Connection> conn(new Connection(argv[1], port));
	if (!conn->isConnected()) {
		cerr << "Connection attempt failed" << endl;
		exit(1);
	}
	ClientParser cp;
	while (true) {
		cout << "Pick a number:" << endl;
		cout << "1: List newsgroups" << endl;
		cout << "2: Create a newsgroup" << endl;
		cout << "3: Delete a newsgroup" << endl;
		cout << "4: List articles in a newsgroup" << endl;
		cout << "5: Create an article" << endl;
		cout << "6: Delete an article" << endl;
		cout << "7: Get an article" << endl;
		cout << "8: Quit" << endl;
		try {
			string command_string;
			getline(cin,command_string);
			int command = stoi(command_string);
			vector<unsigned char> output;
			switch(command) {
			//1. List newsgroups. The reply contains the number of newsgroups followed by the identiﬁcation numbers and titles of the groups.
			//COM_LIST_NG COM_END
			case 1:
				output.push_back(Protocol::COM_LIST_NG);
				break;
			//2. Create a newsgroup. The title of the group is sent as a parameter.
			//COM_CREATE_NG string_p COM_END
			case 2:
			{
				output.push_back(Protocol::COM_CREATE_NG);
				cout << "Choose a name for the newsgroup:" << endl;
				string name;
				getline(cin, name);
				cp.push_string(output, name);
				break;
			}
			//3. Delete a newsgroup. The identiﬁcation number of the group is sent as a parameter.
			//COM_DELETE_NG num_p COM_END
			case 3:
			{
				output.push_back(Protocol::COM_DELETE_NG);
				cout << "Specify the ID of the newsgroup:" << endl;
				string id_string;
				getline(cin, id_string);
				int id = stoi(id_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, static_cast<unsigned int>(id));
				break;
			}
			//4. List articles in a newsgroup. The identiﬁcation number of the group is sent as a parameter.
			//The reply contains the number of articles, followed by the identiﬁcation numbers and titles
			//of the articles.
			//COM_LIST_ART num_p COM_END
			case 4:
			{
				output.push_back(Protocol::COM_LIST_ART);
				cout << "Specify the ID of the newsgroup:" << endl;
				string id_string;
				getline(cin, id_string);
				int id = stoi(id_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, static_cast<unsigned int>(id));
				break;
			}
			//5. Create an article. The identiﬁcation number of the group is sent as a parameter, followed by
			//the article title, author and text.
			//COM_CREATE_ART num_p string_p string_p string_p COM_END
			case 5:
			{
				output.push_back(Protocol::COM_CREATE_ART);
				cout << "Specify the ID of the newsgroup:" << endl;
				string id_string;
				getline(cin, id_string);
				int id = stoi(id_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, id);
				cout << "Specify a title for the article:" << endl;
				string title;
				getline(cin, title);
				cp.push_string(output, title);
				cout << "Specify an author for the article:" << endl;
				string author;
				getline(cin, author);
				cp.push_string(output, author);
				cout << "Write the text of the newsgroup, finish with return:" << endl;
				string text;
				getline(cin, text);
				cp.push_string(output, text);
				break;
			}
			//6. Delete an article. The group and article identiﬁcation numbers are sent as parameters.
			//COM_DELETE_ART num_p num_p COM_END
			case 6:
			{
				output.push_back(Protocol::COM_DELETE_ART);
				cout << "Specify the ID of the newsgroup:" << endl;
				string id_string;
				getline(cin, id_string);
				int id = stoi(id_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, id);
				cout << "Specify the ID of the article:" << endl;
				string art_string;
				getline(cin, art_string);
				id = stoi(art_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, id);
				break;
			}
			//7. Get an article. The group and article identiﬁcation numbers are sent as parameters. The
			//reply contains the title, author, and text of the article.
			//COM_GET_ART num_p num_p COM_END
			case 7:
			{
				output.push_back(Protocol::COM_GET_ART);
				cout << "Specify the ID of the newsgroup:" << endl;
				string id_string;
				getline(cin, id_string);
				int id = stoi(id_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, id);
				cout << "Specify the ID of the article:" << endl;
				string art_string;
				getline(cin, art_string);
				id = stoi(art_string);
				if(id < 0) {
					throw invalid_argument("ID has to be larger than 0");
				}
				cp.push_int(output, id);
				break;
			}
			case 8:
				cout << "Exiting..." << endl;
				exit(0);
			default:
				throw invalid_argument("Existing commands: 1-8");
				break;
			}
			
			output.push_back(Protocol::COM_END);
			for(auto it = output.begin(); it < output.end(); ++it){ //Write to connection
				conn->write(*it);
			}
			string reply = cp.handle_input(conn);
			cout << reply << endl;
		} catch (ConnectionClosedException&) {
			cout << "No reply from server. Exiting." << endl;
			exit(1);
		} catch (invalid_argument& ia) {
			string message(ia.what());
			cerr << "Error: " << ((message == "stoi") ? "Input has to be a valid number" : message) << endl;
		} catch (out_of_range&) {
			cerr << "Out of range input, enter a lower number" << endl;
		} catch (exception& e) {
			cerr << "Unhandled exception: " << e.what() << endl;
			cerr << "Shutting down" << endl;
			exit(1);
		}
		cout << "Press Enter to continue." << endl;
		string foo;
		getline(cin, foo); // Better ways to wait for enter?
	}
}

